/************************************************************************
* SOAM MAIN PROGRAM														*
* loads data, handles spu communication and job dispatch				*
*************************************************************************
*																		*
* Copyright 2009 Universita' degli Studi di Pavia						*
* Laboratorio di Visione Artificiale									*
* http://vision.unipv.it												*
*																		*
* This software is released under the GNU General Public License v.2	*
* You can find a copy of this license either in file license.txt		*
* or at http://www.gnu.org/licenses/gpl-2.0.txt							*
*																		*
* @author: Luca Bianchi													*
* @copyright: Laboratorio di Visione Artificiale						*
************************************************************************/

#include <sched.h>
#include <libspe2.h>
#include <stdio.h>
#include <stdlib.h>
#include <pthread.h>
#include <dirent.h>
#include <errno.h>
#include <sys/wait.h>
#include <malloc_align.h>

#include "landmarks.h"
#include "cbe_defs.h"
#include "commons.h"
#include "load_data.h"
#include "util_func.h"

#if TIMING
	#include <time.h>
#endif


/***********************************/
/** Variables for CELL management **/
/***********************************/

//programs to load
extern spe_program_handle_t searchWinner; /** search winner handle program **/

spe_info spe[16];


/** DMA - control block structure for DMA transfers **/
dma_info dma[16] __attribute__ ((aligned(128)));


/********************************/
/** context specific variables **/
/********************************/
float *landmarks;
float *samples;

/*****************************************/
/** function for pthread initialization **/
/*****************************************/
void *spu_run_function(void *arg){
	spe_info spe;
	
	spe = *((spe_info *) arg);
	
	if(spe_context_run(spe.spe_ctx,&spe.spe_entry,0,&spe.dma,NULL,&spe.spe_stop_info)<0){
		perror("Failed running context");
		exit(1);
	}
	pthread_exit(NULL);
}


/*****************************************/
/** Main function						**/
/*****************************************/
int main(int argc, char **argv){
	int i, spu_threads;
	int desired_landmarks, num_landmarks, freeList;
	int desired_samples, num_samples,sList;


#if TIMING
	clock_t t1;
	clock_t t2;
#endif

	if(argc < 4){ fprintf(OUT,"Usage %s <num_spes> <num_landmarks> <num_samples>\n",argv[0]);exit(1);}

	spu_threads = spe_cpu_info_get(SPE_COUNT_USABLE_SPES,-1);
	if(spu_threads > atoi(argv[1])) spu_threads = atoi(argv[1]);
	fprintf(OUT,"Using %d spes\n",spu_threads);
	
	desired_landmarks = atoi(argv[2]);
	desired_samples = atoi(argv[3]);





	/** INITIALIZE SOAM VARIABLES **/
	freeList = 0;
	num_landmarks = desired_landmarks;

	// Check if Landmarks number is compatible with CELL architecture
	if(num_landmarks%16!=0){
		num_landmarks = ceil(desired_landmarks/16.0f)*16;
		fprintf(OUT,"Landmarks number must me multiple of 16 * 4(float size) in order to mantain alignment.\n--> Changing landmarks number to %d.\n",num_landmarks);
	}
	fprintf(OUT,"Using %d landmarks\n",num_landmarks);


	// Check if Samples number is compatible with CELL architecture
	num_samples =  desired_samples;
//	if(((num_samples*SPACE_DIM*sizeof(float))/spu_threads)%16 != 0){
		//num_samples = ceil(desired_samples*SPACE_DIM/16.0f*16/SPACE_DIM;	
//		num_samples = floor(((num_samples*SPACE_DIM*sizeof(float))/spu_threads)/16.0f)*16;
//		fprintf(OUT,"In order to mantain alignment, (samples number*SPACE_DIM*4)/(number of spu) has to be multiple of 16.\n--> Changing samples number to %d.\n",num_samples);
//	}

	landmarks_setSpaceDimension(SPACE_DIM);
	landmarks = landmarks_new(num_landmarks,&freeList);
	fprintf(OUT,"Landmarks list created.\n");	
	initLandmarks(landmarks,&freeList,desired_landmarks,SPACE_DIM,RADIUS);
	fprintf(OUT,"Landmarks initialized.\n");

	samples = loadSamples(num_samples,SPACE_DIM,RADIUS);
	fprintf(OUT,"Samples loaded.\n");	


#if TIMING
	t1 = clock();
	fprintf(stderr,"searchWinners.c: started Timing.\n");
#endif

	/* Run every thread */
	for(i = 0; i < spu_threads; i++){
		
		spe[i].spe_entry = SPE_DEFAULT_ENTRY;	// you HAVE TO specify default entry. Otherwise it won't work. As I've experienced during a whole debugging afternoon	
		
		dma[i].land_size = num_landmarks*SPACE_DIM*sizeof(float);
		dma[i].sample_size = ((num_samples*SPACE_DIM/spu_threads*sizeof(float)));
		dma[i].land_addr = (unsigned long long) (landmarks);
		dma[i].sample_addr = (unsigned long long) (samples);
		spe[i].dma = dma[i];	
	
		/* Create context */
		if((spe[i].spe_ctx = spe_context_create(0, NULL)) == NULL){
			perror("Failed creating context");
			exit(1);
		}

		/* Load program into context */
		if(spe_program_load(spe[i].spe_ctx,&searchWinner)){
			perror("Failed loading program into context");
			exit(1);
		}
		
		/* Create thread for each spe context */
		if(pthread_create(&spe[i].thread,NULL,&spu_run_function,&spe[i].spe_ctx)){
			perror("Failed creating thread for context");
			exit(1);
		}
	}

	/* Wait for SPU completion */
	for(i=0; i<spu_threads; i++){
		if(pthread_join(spe[i].thread,NULL)){
			perror("Failed joining thread");
			exit(1);
		}



		
		/* destroy the SPE context 
		if (spe_context_destroy(spe[i].spe_ctx) != 0) {
    		fprintf (OUT, "FAILED: spe_context_destroy(errno=%d strerror=%s)\n", errno, strerror(errno));
    		exit (1);
  		}

  		/* check the SPE status *
  		if (spe[i].spe_stop_info.stop_reason == SPE_EXIT) {
    		if (spe[i].spe_stop_info.result.spe_exit_code != 0) {
      			fprintf(OUT, "FAILED: SPE returned a non-zero exit status\n");
      			exit(1);
    		}
  		} else {
    		fprintf(OUT, "FAILED: SPE abnormally terminated\n");
    		exit(1);
  		}
*/
	}
	
#if TIMING
	t2 = clock();
#endif
#if TIMING
	fprintf(stderr, "searchWinners.c: execution Time for %d samples, using %d landmarks: %fs\n",
		num_samples, num_landmarks, ((double) (t2 - t1)) / CLOCKS_PER_SEC);
#endif

	return 0;
}
	

